package com.demo.webapp.service.impl;

import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.demo.core.model.Record;
import com.demo.core.service.base.RecordService;
import com.demo.webapp.service.AccessStatisticsService;

/**
 * 访问统计服务
 * 1、提供
 * @author ryuu.kk
 *
 */
public class AccessStatisticsServiceImpl implements AccessStatisticsService {

	private static final Log Logger = LogFactory.getLog(AccessStatisticsService.class);
	
	// 统计服务
	private RecordService recordService;
	
	// 一个软引用对象
	private SoftReference<ConcurrentMap<Byte, ConcurrentHashMap<Long, Integer>>> softRef = null;
	
	// 访问统计缓存队列
	private static ConcurrentMap<Byte, ConcurrentHashMap<Long, Integer>> queues = null; 
	
	// 定时写入队列(强引用类型)
	private static ConcurrentMap<Byte, ConcurrentHashMap<Long, Integer>> flash = null;
	
	/**
	 * 构造方法
	 * @param types 类型
	 */
	public AccessStatisticsServiceImpl() {
		if (Logger.isDebugEnabled()) {
			Logger.debug("init AccessStatisticsService & RecordQueue");
		}
		// 创建队列
		queues = queueReference();
		
		if (flash == null) {
			flash = new ConcurrentHashMap<Byte, ConcurrentHashMap<Long, Integer>>(){
				/**
				 * 序列化ID 
				 */
				private static final long serialVersionUID = 1L;
			{
				for(TYPE ex : TYPE.values()) {
					// 取得类型
					byte type = getType(ex); 
					put(type, new ConcurrentHashMap<Long, Integer>());
				}
			}};
		}
	}
	
	/**
	 * 记录各种统计(调用一次自增1)
	 * @param objectId 对象id
	 * @param type 类型
	 * @return int 访问统计
	 */
	public int record(long objectId, TYPE e) {
		// 取得类型
		byte type = getType(e); 
		Integer count = 0;
		ConcurrentHashMap<Long, Integer> queue = queueReference().get(type);
		if(queue != null){
			count = queue.get(objectId);
			if (count == null) {
				Record record = recordService.find(objectId, type);
				if (record != null) {
					count = record.getCount();
				} else {
					count = 0;
				}
			}
			count += 1;
			queue.put(objectId, count.intValue());
			System.out.printf("record (type=%d,id=%d,count=%d)\n", type, objectId, count);
		}
		// 设置写入队列
		if (flash != null) {
			ConcurrentMap<Long, Integer> flashMap = flash.get(type);
			flashMap.put(objectId, count.intValue());
		}
		return count;
	}
	
	/**
	 * count 次数
	 * @param objectId 主键ID 
	 * @param e TYPE 类型
	 * @return int 次数
	 */
	public int count(long objectId, TYPE e) {
		// 取得类型
		byte type = getType(e); 
		Integer count = 0;
		ConcurrentHashMap<Long, Integer> queue = queueReference().get(type);
		if(queue != null){
			count = queue.get(objectId);
			if (count == null) {
				Record record = recordService.find(objectId, type);
				if (record != null) {
					count = record.getCount();
				} else {
					count = 0;
				}
			}
			if (e.equals(TYPE.READ)) {
				count += 1;
				// 设置写入队列
				if (flash != null) {
					ConcurrentMap<Long, Integer> flashMap = flash.get(type);
					flashMap.put(objectId, count.intValue());
				}
			}
			queue.put(objectId, count.intValue());
			System.out.printf("record (type=%d,id=%d,count=%d)\n", type, objectId, count);
		}
		return count;
	}
	
	@Override
	public int[] count(long objectId, String[] types) {
		int[] results = null;
		if (types != null) {
			results = new int[types.length];
			for(int i = 0; i < types.length; i ++) {
				String type = types[i];
				results[i] = count(objectId, getType(type)); 
			}
		}
		return results;
	}
	
	@Override
	public int record(long objectId, String type) {
		return record(objectId, getType(type));
	}
	
	/**
	 * 定时写入DB
	 * @return
	 */
	public boolean flush() {
		
		boolean success = true;
		for(TYPE ex : TYPE.values()) {
			byte type = getType(ex); 
			ConcurrentMap<Long, Integer> recordMap = flash.get(type);
			if (recordMap != null) {
				Iterator<Long> iterator = recordMap.keySet().iterator();
				while(iterator.hasNext()) {
					Long id = iterator.next();
					try {
						recordService.reset(null, id, "", recordMap.get(id), type);
					} catch (Exception e) {
						e.printStackTrace();
						success = false;
					}
				}
				recordMap.clear();
			}
		}
//		// 操作成功,清空写入队列
//		if (success) {
//			flash.clear();
//		}
		return success;
	}
	
	private TYPE getType(String type) {
		TYPE e = TYPE.READ;
		if ("READ".equalsIgnoreCase(type)) {
			e = TYPE.READ;
		} else if ("REPLY".equalsIgnoreCase(type)) {
			e = TYPE.REPLY;
		} else if ("UP".equalsIgnoreCase(type)) {
			e = TYPE.UP;
		} else if ("DOWN".equalsIgnoreCase(type)) {
			e = TYPE.DOWN;
		} else if ("FAVOUR".equalsIgnoreCase(type)) {
			e = TYPE.FAVOUR;
		}
		return e;
	}
	
	private byte getType(TYPE e) {
		byte type;
		switch (e) {
		case READ :
			type = 0x01;
			break;
		case REPLY :
			type = 0x02;
			break;
		case UP:
			type = 0x03;
			break;
		case DOWN:
			type = 0x04;
			break;
		case FAVOUR:
			type = 0x05;
			break;
		default :
			type = 0x10;
		}
		return type;
	}
	// ~~ setting here =======================================================================
	private synchronized ConcurrentMap<Byte, ConcurrentHashMap<Long, Integer>> queueReference() {
		if (softRef != null) {
			queues = softRef.get();
		}
		if (queues == null) {
			softRef = new SoftReference<ConcurrentMap<Byte, ConcurrentHashMap<Long, Integer>>>(queues = new ConcurrentHashMap<Byte, ConcurrentHashMap<Long, Integer>>(){
				/**
				 * 序列化ID 
				 */
				private static final long serialVersionUID = 1L;
			{
				for(TYPE ex : TYPE.values()) {
					// 取得类型
					byte type = getType(ex); 
					put(type, new ConcurrentHashMap<Long, Integer>());
				}
			}});
		}
		
		return queues;
	}

	public void setRecordService(RecordService recordService) {
		this.recordService = recordService;
	}
}
